package com.cpu.z80.processor;

/**
 * Zilog Z80 CPU Core Module!
 * @author kennux
 */
import com.cpu.z80.register.*;
import com.memory.Memory;

public class Z80Core {
    private Double8Bit BC,DE,HL;
    private FlagRegister AF;
    private Double8Bit bc,de,hl; // 2. Register Pair
    private Double8Bit IX,IY,IR,PC;
    private Double8Bit SP;
    private Memory Ram;
    private int cycles;
    private int[][] cycleTable = {{4,10,7,6,4,4,7,4}};
    private boolean isRunning;
    Runnable theCore = new Runnable() {
        public void run() {
            while(true) {
                while(isRunning) {
                // Get new OpCode
                int opCode = (int)Ram.get8Bit(PC.get16Bit());
                if(cycles < cycleTable[0][opCode]) {
                    isRunning = false;
                    continue;
                }
                PC.add16Bit((short)1);

                switch (opCode) {
                    case 0xCB:
                        // CB-Interpretation
                        break;
                    case 0xED:
                        // ED-Interpretation
                        break;
                    case 0xDD:
                        // DD-Interpretation
                        break;
                    case 0xFD:
                        // FD-Interpretation
                        break;
                    default:
                        interpret(opCode); // Interpret normal OpCode
                        break;
                    }
            }
            try {
                Thread.sleep(6);
            } catch (java.lang.InterruptedException e) {

            }
        }
    }
};

    private void interpret(int opCode) {
        switch(opCode) {
            case 0x00:
                // NOP
                break;
            case 0x01:
                // LD bc,nn
                BC.set16Bit(Ram.get16Bit(PC.get16Bit()));
                PC.add16Bit((short)2);
                break;
            case 0x02:
                // LD (BC), a
                Ram.set8Bit(BC.get16Bit(), AF.get8Bit(1));
                // Flags
                /*AF.clearHalfCarry(); // Half-Carry Flag Unset
                AF.clearAddSubtract(); // Add/Subtract Flag
                AF.calcZero((int)(A.get8Bit(1))); // Zero-Flag
                AF.calcSign((int)A.get8Bit(1)); // Sign-Flag
                AF.clearOverflow(); // Overflow-Flag*/
                break;
            case 0x03:
                // INC BC
                BC.add16Bit((short)1);
                break;
            case 0x04:
                // INC B
                BC.add8Bit((byte)1, 1);
                flagsInc8(BC.get8Bit(1));
                break;
            case 0x05:
                // DEC B
                BC.sub8Bit((byte)1, 1);
                flagsDec8(BC.get8Bit(1));
                break;
            case 0x06:
                // LD B, N
                BC.set8Bit(Ram.get8Bit(PC.get16Bit()), 1);
                PC.add16Bit((short)1);
                break;
            case 0x07:
                // RLCA
                if(AF.getHalfCarry()) {
                    AF.setHalfCarry();
                } else {
                    AF.clearHalfCarry();
                }
                AF.setBit(5);
                AF.set8Bit((byte)(AF.get8Bit(1) >> 7), 1);
                AF.set8Bit((byte)((AF.get8Bit(2) << 1) | AF.get8Bit(1)), 1);
                break;
        }
        cycles -= cycleTable[0][opCode]; // 4 Cycles
    }

    public void addCycles(int count) {
        cycles += count;
        System.out.println("Added Cycles " + cycles + " " + count);
    }


    public Z80Core() {
        BC = new Double8Bit();
        DE = new Double8Bit();
        HL = new Double8Bit();
        AF = new FlagRegister();
        bc = new Double8Bit();
        de = new Double8Bit();
        hl = new Double8Bit();
        SP = new Double8Bit();
        IX = new Double8Bit();
        IY = new Double8Bit();
        IR = new Double8Bit();
        PC = new Double8Bit();
    }

    public void startCpu(short BinAdress, Memory mem) {
        PC.set16Bit(BinAdress);
        Ram = mem;
        System.err.println("Z80 Core started at adress " + BinAdress);
        Thread t = new Thread(theCore);
        t.start();
    }

    private void push8Bit(byte value) {
        SP.add16Bit((short)1);
        Ram.set8Bit(SP.get16Bit(), value);
    }
    private void push16Bit(short value) {
        SP.add16Bit((short)2);
        Ram.set16Bit(SP.get16Bit(),value);
    }
    private byte pop8Bit() {
        SP.sub16Bit((short)1);
        return Ram.get8Bit(SP.get16Bit());
    }
    private short pop16Bit() {
        SP.sub16Bit((short)2);
        return Ram.get16Bit(SP.get16Bit());
    }

    private void flagsInc8(byte value) {
        AF.calcZero((int)value);
        AF.calcSign((int)value);
        AF.calcHalfCarry8(value);
        AF.setOverflow();
        AF.clearAddSubtract();
    }
    private void flagsDec8(byte value) {
        AF.calcZero((int)value);
        AF.calcSign((int)value);
        AF.calcHalfCarry8(value);
        AF.setOverflow();
        AF.setAddSubtract();
    }
}
